package kr.pe.johnkim.pushchat.mobile.controller;

import kr.pe.johnkim.pushchat.mobile.domain.PushChatSMS;
import kr.pe.johnkim.pushchat.mobile.service.PushChatCertificationService;
import kr.pe.johnkim.pushchat.mobile.util.PushChatCommon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.TaskExecutor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * @author : John_Kim <cale9797@gmail.com>
 * @date : 13. 6. 27.
 */
@Controller
@RequestMapping(value = "/pushChatCertificationController")
public class PushChatCertificationController {

    // SLF4J Logging
    private Logger logger = LoggerFactory.getLogger(PushChatCertificationController.class);

    @Autowired
    private PushChatCertificationService pushChatCertificationService;

    @Autowired
    private PushChatCommon pushChatCommon;

    @Autowired
    private TaskExecutor taskExecutor;

    // 승인 요청 메소드 호출 여부.
    private boolean REQUEST_ADMISSION;

    // taskExecutor 실행 횟수 확인.
    private int REQUEST_INTERVAL = 0;

    // 애플 리뷰용 휴대전화번호.
    private @Value("${review.phone.number}") String reviewPhoneNumber;

    // 애플 리뷰용 인증 번호.
    private @Value("${review.certification.number}") String reviewCertificationNumber;

    /**
     * 해당 사용자가 인증 번호를 요청 한다.
     * (만약 기존 인증요청이 있을 경우 기존 정보를 비활성화 한다.)
     *
     * 1. 인증 번호 생성.
     * 2. 생성한 인증번호를 database 에 저장.
     * 3. 생성한 인증번호를 해당 사용자에게 SMS 로 전송.
     * 4. 2분동안 사용자의 승인요청을 기다린다.
     * 5. 만약 2분 이내에 사용자가 승인요청을 하지 않으면 기존 인증을 비활성화 하고 새로운 인증번호를 요청하라는 SMS 를 전송 한다.
     *
     * ----------------------
     *
     * 애플 리뷰를 위한 테스트 로직 추가.
     *
     * 외국일 경우 SMS 가 불가하기 때문에 리뷰를 위한 테스트 로직을 추가 한다.
     * 리뷰를 위한 별도의 휴대전화번호와 인증번호를 제공하기 때문에
     * 휴대폰 전화번호 '01012345678' 이 들어올 경우 아래의 로직을 skip 하고 "SUCCESS" 를 리턴한다.
     * (01012345678 은 실제 사용자가 없을것이라고 가정.)
     *
     * 휴대전화번호 : 01012345678
     * 인증번호    : 6989
     *
     * @param pushChatSMS
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/requestCertification", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public PushChatSMS requestCertification(@RequestBody final PushChatSMS pushChatSMS) throws Exception {
        logger.info("requestCertification");

        PushChatSMS result = new PushChatSMS();

        if((reviewPhoneNumber).equals(pushChatSMS.getPhoneNumber())) {
            /*
            애플 리뷰용 휴대전화 번호가 들어올 경우 인증 로직을 skip 하고 "SUCCESS" 를 반환 한다.
             */
            result.setResult("SUCCESS");
        } else {
            /*
            사용자가 인증 번호 요청 버튼을 연속으로 누를 수도 있기 때문에
            기존에 요청한 정보가 있는지 확인하고, 있으면 비활성화 시킨 후 다시 인증 번호를 송신 한다.
         */
            try {
                if(("1").equals(this.pushChatCertificationService.selectExistCertificationInformation(pushChatSMS).getTotalCount())) {
                    logger.info("**********");
                    logger.info("already exist user's request certification. so deactivate before request.");
                    logger.info("**********");

                    PushChatSMS resultCertification = this.pushChatCertificationService.selectCertificationInformation(pushChatSMS);

                    String idPushChatCertification = resultCertification.getIdPushChatCertification();

                    // 기존 인증번호를 비활성화 한다.
                    PushChatSMS parameter = new PushChatSMS();
                    parameter.setIdPushChatCertification(idPushChatCertification);
                    parameter.setActivateYn("N");
                    parameter.setModifyDate(this.pushChatCommon.getCurrentDate());

                    logger.info("idWaitingCertifications : {}", parameter.getIdPushChatCertification());
                    logger.info("activateYn : {}", parameter.getActivateYn());
                    logger.info("modifyDate : {}", parameter.getModifyDate());

                    this.pushChatCertificationService.updateDeactivateCertification(parameter);
                }
            } catch (Exception e) {
                logger.info("error occurred while deactivate user before request.");
            }

            REQUEST_ADMISSION = true;

            // 인증 번호를 생성 한다.
            String certificationNumber = String.valueOf(this.pushChatCommon.getCertificationNumber());

            logger.info("certificationNumber : {}", certificationNumber);

            // 가져온 인증 번호를 세팅 한다.
            pushChatSMS.setCertificationNumber(certificationNumber);

            logger.info("phoneNumber         : {}", pushChatSMS.getPhoneNumber());
            logger.info("certificationNumber : {}", pushChatSMS.getCertificationNumber());
            logger.info("activateYn          : {}", pushChatSMS.getActivateYn());
            logger.info("modifyDate          : {}", pushChatSMS.getModifyDate());
            logger.info("registerDate        : {}", pushChatSMS.getRegisterDate());

            try {
                this.pushChatCertificationService.registerCertification(pushChatSMS);

                // 인증 번호를 SMS 로 전송 한다.
                PushChatSMS parameterSMS = new PushChatSMS();
                parameterSMS.setMsg("Tong 사용자 인증번호 [" + certificationNumber + "] 를 입력하세요.");
                parameterSMS.setRphone(pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));
                parameterSMS.setSphone1("02");
                parameterSMS.setSphone2("3775");
                parameterSMS.setSphone3("3799");
                parameterSMS.setRdate("");
                parameterSMS.setRtime("");
                parameterSMS.setTestflag("");
                parameterSMS.setDestination(pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));
                parameterSMS.setRepeatFlag("");
                parameterSMS.setRepeatNum("");
                parameterSMS.setRepeatTime("");
                parameterSMS.setNointeractive("");

                logger.info("sms : {}", pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));

                this.pushChatCommon.sendSMS(parameterSMS);

                result.setResult("SUCCESS");
            } catch (Exception e) {
                result.setResult("FAIL");
            }

        /*
            Thread 가 실행되면서 2분안에 requestAdmission 메소드가 호출되는지 확인 한다.
            만약 호출되지 않으면 기존 인증번호를 비활성화 하고 새로운 인증번호를 요청하라는 SMS 를 발송 한다.
         */
            this.taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    logger.info("taskExecutor start : {}", pushChatSMS.getPhoneNumber());

                    // 1초에 한번씩 REQUEST_ADMISSION 이 false 로 업데이트 되었는지 확인 한다.
                    while (true) {
                        try {
                            Thread.sleep(1000);
                            REQUEST_INTERVAL++;
                        } catch (Exception e) {
                            logger.error("error occurred on taskExecutor.");
                        }

                        // Thread 는 2분간 작동하면서 requestAdmission 메소드가 호출되었는지 확인 한다.
                        if(REQUEST_INTERVAL != 120) {
                            // 만약 requestAdmission 메소드가 호출되었으면 thread 를 종료 한다.
                            if(!REQUEST_ADMISSION) {
                                logger.info("taskExecutor stop.");

                                // taskExecutor 실행 횟수 확인 초기화.
                                REQUEST_INTERVAL = 0;

                                break;
                            }
                        } else {
                            // 만약 2분내에 requestAdmission 메소드가 호출되지 않으면 인증 요청을 취소하고 사용자에게 SMS 를 전송 한다.
                            if(REQUEST_ADMISSION) {
                                REQUEST_ADMISSION = false;

                                // 기존 인증번호를 비활성화 한다.
                                // 저장된 사용자의 인증 정보를 가져 온다.
                                PushChatSMS resultCertification = pushChatCertificationService.selectCertificationInformation(pushChatSMS);

                                String idPushChatCertification = resultCertification.getIdPushChatCertification();

                                PushChatSMS parameter = new PushChatSMS();
                                parameter.setIdPushChatCertification(idPushChatCertification);
                                parameter.setActivateYn("N");
                                parameter.setModifyDate(pushChatCommon.getCurrentDate());

                                logger.info("idWaitingCertifications : {}", parameter.getIdPushChatCertification());
                                logger.info("activateYn : {}", parameter.getActivateYn());
                                logger.info("modifyDate : {}", parameter.getModifyDate());

                                pushChatCertificationService.updateDeactivateCertification(parameter);

                                // "시간초과. 인증번호를 다시 요청하세요." 메시지를 SMS 로 발송 한다.
                                PushChatSMS parameterSMS = new PushChatSMS();
                                parameterSMS.setMsg("시간초과. 인증번호를 다시 요청하세요.");
                                parameterSMS.setRphone(pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));
                                parameterSMS.setSphone1("02");
                                parameterSMS.setSphone2("3775");
                                parameterSMS.setSphone3("3799");
                                parameterSMS.setRdate("");
                                parameterSMS.setRtime("");
                                parameterSMS.setTestflag("");
                                parameterSMS.setDestination(pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));
                                parameterSMS.setRepeatFlag("");
                                parameterSMS.setRepeatNum("");
                                parameterSMS.setRepeatTime("");
                                parameterSMS.setNointeractive("");

                                logger.info("sms : {}", pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));

                                try {
                                    pushChatCommon.sendSMS(parameterSMS);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }

                                // taskExecutor 실행 횟수 확인 초기화.
                                REQUEST_INTERVAL = 0;

                                break;
                            }
                        }
                    }
                }
            });
        }

        return result;
    }

    /**
     * 해당 사용자가 SMS 로 받은 인증번호를 보내서 인증 허가를 요청 한다.
     *
     * 1. database 의 인증 정보와 비교 한다.
     * 2. 결과를 반환해서 인증 프로세스를 마친다.
     *
     * -------------------------------------------------
     *
     * 애플 리뷰를 위한 테스트 로직 추가.
     *
     * 애플 리뷰용 휴대전화 번호와 인증 번호가 들어올 경우 아래의 승인 로직을 skip 하고 "SUCCESS_ADMISSION" 을 리턴 한다.
     * 리뷰용 휴대전화 번호가 잘못 입력되고 인증 번호만 맞게 입력될 경우 "FAIL" 을 리턴 한다.
     * 모든 client 에서는 "SUCCESS_ADMISSION" 가 리턴될 경우 alert 을 출력하고 다시 상용자 입력을 받는다.
     *
     * @param pushChatSMS
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/requestAdmission", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public PushChatSMS requestAdmission(@RequestBody PushChatSMS pushChatSMS) throws Exception {
        logger.info("requestAdmission");

        logger.info("phoneNumber         : {}", pushChatSMS.getPhoneNumber());
        logger.info("certificationNumber : {}", pushChatSMS.getCertificationNumber());
        logger.info("activateYn          : {}", pushChatSMS.getActivateYn());

        PushChatSMS result = new PushChatSMS();

        if(((reviewPhoneNumber).equals(pushChatSMS.getPhoneNumber())) &&
                ((reviewCertificationNumber).equals(pushChatSMS.getCertificationNumber()))) {
            /*
            애플 리뷰용 휴대전화 번호와 인증 번호일 경우 승인 로직을 skip 하고 "SUCCESS_ADMISSION" 을 리턴 한다.
             */
            result.setResult("SUCCESS_ADMISSION");
        } else if(((reviewPhoneNumber).equals(pushChatSMS.getPhoneNumber())) &&
                (!(reviewCertificationNumber).equals(pushChatSMS.getCertificationNumber()))) {
            /*
            만약 애플 리뷰용 휴대전화 번호는 일치하는데 실수로 인증 번호가 틀리게 입력되었을 경우.
            인증 번호는 랜덤으로 생성되기 때문에 리뷰용 휴대전화 번호는 잘못 입력되고 인증 번호는 맞게 입력되는 경우는 고려하지 않는다.
            "SUCCESS_ADMISSION" 이외의 값이 리턴될 경우 client 에서는 alert 창을 띄우고 다시 사용자 입력을 받는다.
             */
            result.setResult("FAIL");
        } else {
            /*
            애플 리뷰용 휴대전화 번호가 아닌 모든 경우 인증 허가를 요청 한다.
             */
            try {
                REQUEST_ADMISSION = false;

                // 저장된 사용자의 인증 정보가 있는지 확인 한다.
                if(("1").equals(this.pushChatCertificationService.selectExistCertificationInformation(pushChatSMS).getTotalCount())) {
                    // 저장된 사용자의 인증 정보를 가져 온다.
                    PushChatSMS resultCertification = this.pushChatCertificationService.selectCertificationInformation(pushChatSMS);
                    String idPushChatCertifications = resultCertification.getIdPushChatCertification();
                    String certificationNumber = resultCertification.getCertificationNumber();

                    logger.info("idWaitingCertifications         : {}", idPushChatCertifications);
                    logger.info("certificationNumber             : {}", certificationNumber);
                    logger.info("input value certificationNumber : {}", pushChatSMS.getCertificationNumber());

                    if((certificationNumber).equals(pushChatSMS.getCertificationNumber())) {
                        // 로직 성공. 인증 번호 일치.
                        result.setResult("SUCCESS_ADMISSION");
                    } else {
                        // 로직 성공. 인증 번호 불일치.
                        result.setResult("SUCCESS_NOT_ADMISSION");
                    }

                    // 기존 인증번호를 비활성화 한다.
                    PushChatSMS parameter = new PushChatSMS();
                    parameter.setIdPushChatCertification(idPushChatCertifications);
                    parameter.setActivateYn("N");
                    parameter.setModifyDate(this.pushChatCommon.getCurrentDate());

                    logger.info("idPushChatCertifications : {}", parameter.getIdPushChatCertification());
                    logger.info("activateYn               : {}", parameter.getActivateYn());
                    logger.info("modifyDate               : {}", parameter.getModifyDate());

                    this.pushChatCertificationService.updateDeactivateCertification(parameter);
                } else {
                    // 저장된 사용자의 인증 정보가 없다.
                    result.setResult("SUCCESS_NON_EXIST");

                    PushChatSMS parameterSMS = new PushChatSMS();
                    parameterSMS.setMsg("일치하는 인증정보가 없습니다. 다시 시도하세요.");
                    parameterSMS.setRphone(pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));
                    parameterSMS.setSphone1("02");
                    parameterSMS.setSphone2("3775");
                    parameterSMS.setSphone3("3799");
                    parameterSMS.setRdate("");
                    parameterSMS.setRtime("");
                    parameterSMS.setTestflag("");
                    parameterSMS.setDestination(pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));
                    parameterSMS.setRepeatFlag("");
                    parameterSMS.setRepeatNum("");
                    parameterSMS.setRepeatTime("");
                    parameterSMS.setNointeractive("");

                    logger.info("sms : {}", pushChatCommon.getSMSPhoneNumber(pushChatSMS.getPhoneNumber()));

                    try {
                        pushChatCommon.sendSMS(parameterSMS);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } catch (Exception e) {
                logger.error("FAIL");

                // 로직 실패.
                result.setResult("FAIL");
            }
        }

        return result;
    }
}
